home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 27 / CU Amiga Magazine's Super CD-ROM 27 (1998)(EMAP Images)(GB)[!][issue 1998-10].iso / CUCD / PowerPC / vbcc / machines / amigawos / libsrc / stdio / vfscanf.c < prev    next >
C/C++ Source or Header  |  1998-08-02  |  10KB  |  394 lines

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdarg.h>
  4. #include <limits.h>
  5. #include <ctype.h>
  6. #include <math.h>
  7.  
  8. /* some macros to cut this short
  9.  * NEXT(c);     read next character
  10.  * PREV(c);     ungetc a character
  11.  * VAL(a)       leads to 1 if a is true and valid
  12.  */
  13. #define NEXT(c) ((c)=getc(stream),size++,incount++)
  14. #define PREV(c) do{if((c)!=EOF)ungetc((c),stream);size--;incount--;}while(0)
  15. #define VAL(a)  ((a)&&size<=width)
  16.  
  17. #ifdef MATH
  18. static unsigned char undef[3][sizeof(double)]= /* Undefined numeric values, IEEE */
  19. { { 0x7f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00 }, /* +inf */
  20.   { 0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00 }, /* -inf */
  21.   { 0x7f,0xf1,0x00,0x00,0x00,0x00,0x00,0x00 }  /*  NaN */
  22. };
  23. #endif
  24.  
  25. int vfscanf(FILE *stream,const char *format,va_list args)
  26. {
  27.   size_t blocks=0,incount=0;
  28.   int c=0;
  29.  
  30.   while(*format)
  31.   {
  32.     size_t size=0;
  33.  
  34.     if(*format=='%')
  35.     {
  36.       size_t width=ULONG_MAX;
  37.       char type,subtype='i',ignore=0;
  38.       const unsigned char *ptr=format+1;
  39.       size_t i;
  40.  
  41.       if(isdigit(*ptr))
  42.       { width=0;
  43.         while(isdigit(*ptr))
  44.           width=width*10+(*ptr++-'0'); }
  45.  
  46.       while(*ptr=='h'||*ptr=='l'||*ptr=='L'||*ptr=='*')
  47.       { if(*ptr=='*')
  48.           ignore=1;
  49.         else
  50.           subtype=*ptr;
  51.         ptr++;
  52.       }
  53.  
  54.       type=*ptr++;
  55.  
  56.       if(type&&type!='%'&&type!='c'&&type!='n'&&type!='[')
  57.       { do /* ignore leading whitespace characters */
  58.           NEXT(c);
  59.         while(isspace(c));
  60.         size=1; } /* The first non-whitespace character is already read */
  61.  
  62.       switch(type)
  63.       { case 'c':
  64.         { unsigned char *bp;
  65.  
  66.           if(width==ULONG_MAX) /* Default */
  67.             width=1;
  68.  
  69.           if(!ignore)
  70.             bp=va_arg(args,char *);
  71.           else
  72.             bp=NULL; /* Just to get the compiler happy */
  73.  
  74.           NEXT(c); /* 'c' did not skip whitespace */
  75.           while(VAL(c!=EOF))
  76.           { if(!ignore)
  77.               *bp++=c;
  78.             NEXT(c);
  79.           }
  80.           PREV(c);
  81.  
  82.           if(!ignore&&size)
  83.             blocks++;
  84.           break;
  85.         }
  86.         case '[':
  87.         { unsigned char *bp;
  88.           unsigned char tab[32],a,b;
  89.           char circflag=0;
  90.  
  91.           if(*ptr=='^')
  92.           { circflag=1;
  93.             ptr++; }
  94.           for(i=0;i<sizeof(tab);i++)
  95.             tab[i]=circflag?255:0;
  96.           do
  97.           { if(!*ptr)
  98.               break;
  99.             a=*ptr++;
  100.             if(*ptr=='-'&&ptr[1]>a)
  101.             { ptr++;
  102.               b=*ptr++;
  103.             }else
  104.               b=a;
  105.             for(i=a;i<=b;i++)
  106.               if(circflag)
  107.                 tab[i/8]&=~(1<<(i&7));
  108.               else
  109.                 tab[i/8]|=1<<(i&7);
  110.           }while(*ptr!=']');
  111.  
  112.           if(!ignore)
  113.             bp=va_arg(args,char *);
  114.           else
  115.             bp=NULL; /* Just to get the compiler happy */
  116.  
  117.           NEXT(c);
  118.           while(VAL(c!=EOF&&tab[c/8]&(1<<(c&7))))
  119.           { if(!ignore)
  120.               *bp++=c;
  121.             NEXT(c);
  122.           }
  123.           PREV(c);
  124.  
  125.           if(!ignore&&size)
  126.           { *bp++='\0';
  127.             blocks++; }
  128.           break;
  129.         }
  130.         case 's':
  131.         { unsigned char *bp;
  132.  
  133.           if(!ignore)
  134.             bp=va_arg(args,char *);
  135.           else
  136.             bp=NULL; /* Just to get the compiler happy */
  137.  
  138.           while(VAL(c!=EOF&&!isspace(c)))
  139.           { if(!ignore)
  140.               *bp++=c;
  141.             NEXT(c);
  142.           }
  143.           PREV(c);
  144.  
  145.           if(!ignore&&size)
  146.           { *bp++='\0';
  147.             blocks++; }
  148.           break;
  149.         }
  150. #ifdef MATH
  151.         case 'e':
  152.         case 'f':
  153.         case 'g':
  154.         { double v;
  155.           int ex=0;
  156.           int min=0,mine=0; /* This is a workaround for gcc 2.3.3: should be char */
  157.  
  158.           do /* This is there just to be able to break out */
  159.           {
  160.             if(VAL(c=='-'||c=='+'))
  161.             { min=c;
  162.               NEXT(c); }
  163.  
  164.             if(VAL(tolower(c)=='i')) /* +- inf */
  165.             { int d;
  166.               NEXT(d);
  167.               if(VAL(tolower(d)=='n'))
  168.               { int e;
  169.                 NEXT(e);
  170.                 if(VAL(tolower(e)=='f'))
  171.                 { v=*(double *)&undef[min=='-'];
  172.                   break; } /* break out */
  173.                 PREV(e);
  174.               }
  175.               PREV(d);
  176.             }
  177.             else if(VAL(toupper(c)=='N')) /* NaN */
  178.             { int d;
  179.               NEXT(d);
  180.               if(VAL(tolower(d)=='a'))
  181.               { int e;
  182.                 NEXT(e);
  183.                 if(VAL(toupper(e)=='N'))
  184.                 { v=*(double *)&undef[2];
  185.                   break; }
  186.                 PREV(e);
  187.               }
  188.               PREV(d);
  189.             }
  190.  
  191.             v=0.0;
  192.             while(VAL(isdigit(c)))
  193.             { v=v*10.0+(c-'0');
  194.               NEXT(c);
  195.             }
  196.  
  197.             if(VAL(c=='.'))
  198.             { double dp=0.1;
  199.               NEXT(c);
  200.               while(VAL(isdigit(c)))
  201.               { v=v+dp*(c-'0');
  202.                 dp=dp/10.0;
  203.                 NEXT(c); }
  204.               if(size==2+(min!=0)) /* No number read till now -> malformatted */
  205.               { PREV(c);
  206.                 c='.'; }
  207.             }
  208.  
  209.             if(min&&size==2) /* No number read till now -> malformatted */
  210.             { PREV(c);
  211.               c=min; }
  212.             if(size==1)
  213.               break;
  214.  
  215.             if(VAL(tolower(c)=='e'))
  216.             { int d;
  217.               NEXT(d);
  218.               if(VAL(d=='-'||d=='+'))
  219.               { mine=d;
  220.                 NEXT(d); }
  221.  
  222.               if(VAL(isdigit(d)))
  223.               { do
  224.                 { ex=ex*10+(d-'0');
  225.                   NEXT(d);
  226.                 }while(VAL(isdigit(d)&&ex<100));
  227.                 c=d;
  228.               }else
  229.               { PREV(d);
  230.                 if(mine)
  231.                   PREV(mine);
  232.               }
  233.             }
  234.             PREV(c);
  235.  
  236.             if(mine=='-')
  237.               v=v/pow(10.0,ex);
  238.             else
  239.               v=v*pow(10.0,ex);
  240.  
  241.             if(min=='-')
  242.               v=-v;
  243.  
  244.           }while(0);
  245.  
  246.           if(!ignore&&size)
  247.           { switch(subtype)
  248.             { case 'l':
  249.               case 'L':
  250.                 *va_arg(args,double *)=v;
  251.                 break;
  252.               case 'i':
  253.                 *va_arg(args,float *)=v;
  254.                 break;
  255.             }
  256.             blocks++;
  257.           }
  258.           break;
  259.         }
  260. #endif
  261.         case '%':
  262.           NEXT(c);
  263.           if(c!='%')
  264.             PREV(c); /* unget non-'%' character */
  265.           break;
  266.         case 'n':
  267.           if(!ignore)
  268.             *va_arg(args,int *)=incount;
  269.           size=1; /* fake a valid argument */
  270.           blocks++;
  271.           break;
  272.         default:
  273.         { unsigned long v=0;
  274.           int base;
  275.           int min=0;
  276.  
  277.           if(!type)
  278.             ptr--; /* unparse NUL character */
  279.  
  280.           if(type=='p')
  281.           { subtype='l'; /* This is the same as %lx */
  282.             type='x'; }
  283.  
  284.           if(VAL((c=='-'&&type!='u')||c=='+'))
  285.           { min=c;
  286.             NEXT(c); }
  287.  
  288.           if(type=='i') /* which one to use ? */
  289.           { if(VAL(c=='0')) /* Could be octal or sedecimal */
  290.             { int d;
  291.               NEXT(d); /* Get a look at next character */
  292.               if(VAL(tolower(d)=='x'))
  293.               { int e;
  294.                 NEXT(e); /* And the next */
  295.                 if(VAL(isxdigit(c)))
  296.                   type='x'; /* Is a valid x number with '0x?' */
  297.                 PREV(e);
  298.               }else
  299.                 type='o';
  300.               PREV(d);
  301.             }else if(VAL(!isdigit(c)&&isxdigit(c)))
  302.               type='x'; /* Is a valid x number without '0x' */
  303.           }
  304.  
  305.           while(type=='x'&&VAL(c=='0')) /* sedecimal */
  306.           { int d;
  307.             NEXT(d);
  308.             if(VAL(tolower(d)=='x'))
  309.             { int e;
  310.               NEXT(e);
  311.               if(VAL(isxdigit(e)))
  312.               { c=e;
  313.                 break; } /* Used while just to do this ;-) */
  314.               PREV(e);
  315.             }
  316.             PREV(d);
  317.             break; /* Need no loop */
  318.           }
  319.  
  320.           base=type=='x'||type=='X'?16:(type=='o'?8:10);
  321.           while(VAL(isxdigit(c)&&(base!=10||isdigit(c))&&(base!=8||c<='7')))
  322.           { v=v*base+(isdigit(c)?c-'0':0)+(isupper(c)?c-'A'+10:0)+(islower(c)?c-'a'+10:0);
  323.             NEXT(c);
  324.           }
  325.  
  326.           if(min&&size==2) /* If there is no valid character after sign, unget last */
  327.           { PREV(c);
  328.             c=min; }
  329.  
  330.           PREV(c);
  331.  
  332.           if(ignore||!size)
  333.             break;
  334.  
  335.           if(type=='u')
  336.             switch(subtype)
  337.             { case 'l':
  338.               case 'L':
  339.                 *va_arg(args,unsigned long *)=v;
  340.                 break;
  341.               case 'i':
  342.                 *va_arg(args,unsigned int *)=v;
  343.                 break;
  344.               case 'h':
  345.                 *va_arg(args,unsigned short *)=v;
  346.                 break;
  347.             }
  348.           else
  349.           { signed long v2;
  350.             if(min=='-')
  351.               v2=-v;
  352.             else
  353.               v2=v;
  354.             switch(subtype)
  355.             { case 'l':
  356.               case 'L':
  357.                 *va_arg(args,signed long *)=v2;
  358.                 break;
  359.               case 'i':
  360.                 *va_arg(args,signed int *)=v2;
  361.                 break;
  362.               case 'h':
  363.                 *va_arg(args,signed short *)=v2;
  364.                 break;
  365.             }
  366.           }
  367.           blocks++;
  368.           break;
  369.         }
  370.       }
  371.       format=ptr;
  372.     }else
  373.     { if(isspace(*format))
  374.       { do
  375.           NEXT(c);
  376.         while(isspace(c));
  377.         PREV(c);
  378.         size=1; }
  379.       else
  380.       { NEXT(c);
  381.         if(c!=*format)
  382.           PREV(c); }
  383.       format++;
  384.     }
  385.     if(!size)
  386.       break;
  387.   }
  388.  
  389.   if(c==EOF&&!blocks)
  390.     return c;
  391.   else
  392.     return blocks;
  393. }
  394.